libobs_simple\sources\linux\sources/linux_general_window_capture.rs
1use libobs_wrapper::{
2 data::ObsObjectBuilder,
3 runtime::ObsRuntime,
4 sources::ObsSourceRef,
5 utils::{ObsError, SourceInfo},
6};
7
8use super::DisplayServerType;
9use crate::sources::linux::{
10 sources::xcomposite_input::XCompositeInputSourceBuilder, PipeWireWindowCaptureSourceBuilder,
11};
12
13/// General Linux window capture source that automatically selects the best capture method.
14///
15/// This wrapper automatically chooses between:
16/// - **PipeWire capture** (for Wayland - captures via desktop portal with window selection)
17/// - **XComposite window capture** (for traditional X11 setups - direct window capture)
18///
19/// The selection is based on the detected display server type.
20///
21/// # Example
22///
23/// ```no_run
24/// use libobs_simple::sources::linux::LinuxGeneralWindowCapture;
25/// use libobs_wrapper::{context::ObsContext, sources::ObsSourceBuilder, utils::StartupInfo};
26///
27/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
28/// # let startup_info = StartupInfo::default();
29/// # let mut context = ObsContext::new(startup_info)?;
30/// # let mut scene = context.scene("Main Scene")?;
31///
32/// // Automatically selects PipeWire or XComposite based on display server
33/// let capture = LinuxGeneralWindowCapture::auto_detect(
34/// context.runtime().clone(),
35/// "Window Capture"
36/// )?;
37///
38/// // Add to scene
39/// scene.add(&capture)?;
40/// # Ok(())
41/// # }
42/// ```
43#[derive(Debug)]
44pub struct LinuxGeneralWindowCapture {
45 info: SourceInfo,
46 capture_type: CaptureType,
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50enum CaptureType {
51 PipeWire,
52 XComposite,
53}
54
55impl LinuxGeneralWindowCapture {
56 /// Create a window capture source by auto-detecting the display server type.
57 ///
58 /// This is the recommended way to create a window capture on Linux.
59 #[must_use = "Use the 'add_to_scene' method to add the source to a scene"]
60 pub fn auto_detect(
61 runtime: ObsRuntime,
62 name: &str,
63 ) -> Result<Self, Box<dyn std::error::Error>> {
64 let display_type = DisplayServerType::detect();
65 Self::new(runtime, name, display_type)
66 }
67
68 /// Create a window capture source for a specific display server type.
69 ///
70 /// # Arguments
71 ///
72 /// * `runtime` - The OBS runtime
73 /// * `name` - Name for the source
74 /// * `display_type` - The display server type to create a source for
75 pub fn new(
76 runtime: ObsRuntime,
77 name: &str,
78 display_type: DisplayServerType,
79 ) -> Result<Self, Box<dyn std::error::Error>> {
80 if display_type.prefer_pipewire() {
81 Self::new_pipewire(runtime, name)
82 } else {
83 Self::new_xcomposite(runtime, name)
84 }
85 }
86
87 /// Create a PipeWire-based window capture source.
88 ///
89 /// Note: On Wayland, window selection is handled by the desktop portal
90 /// which will prompt the user to select a window.
91 pub fn new_pipewire(
92 runtime: ObsRuntime,
93 name: &str,
94 ) -> Result<Self, Box<dyn std::error::Error>> {
95 let builder = PipeWireWindowCaptureSourceBuilder::new(name, runtime.clone())?;
96 let info = builder.set_show_cursor(true).build()?;
97 Ok(LinuxGeneralWindowCapture {
98 info,
99 capture_type: CaptureType::PipeWire,
100 })
101 }
102
103 /// Create an XComposite-based window capture source.
104 ///
105 /// # Arguments
106 ///
107 /// * `runtime` - The OBS runtime
108 /// * `name` - Name for the source
109 pub fn new_xcomposite(
110 runtime: ObsRuntime,
111 name: &str,
112 ) -> Result<Self, Box<dyn std::error::Error>> {
113 let builder = XCompositeInputSourceBuilder::new(name, runtime.clone())?;
114 let info = builder.set_show_cursor(true).build()?;
115 Ok(LinuxGeneralWindowCapture {
116 info,
117 capture_type: CaptureType::XComposite,
118 })
119 }
120
121 /// Create an XComposite-based window capture for a specific window.
122 ///
123 /// # Arguments
124 ///
125 /// * `runtime` - The OBS runtime
126 /// * `name` - Name for the source
127 /// * `window_id` - The X11 window ID to capture
128 pub fn new_xcomposite_with_window(
129 runtime: ObsRuntime,
130 name: &str,
131 window_id: &str,
132 ) -> Result<Self, Box<dyn std::error::Error>> {
133 let builder = XCompositeInputSourceBuilder::new(name, runtime.clone())?;
134 let info = builder
135 .set_capture_window(window_id.to_string())
136 .set_show_cursor(true)
137 .build()?;
138 Ok(LinuxGeneralWindowCapture {
139 info,
140 capture_type: CaptureType::XComposite,
141 })
142 }
143
144 pub fn add_to_scene(
145 self,
146 scene: &mut libobs_wrapper::scenes::ObsSceneRef,
147 ) -> Result<ObsSourceRef, ObsError> {
148 scene.add_source(self.info)
149 }
150
151 /// Get the type of capture being used.
152 pub fn capture_type_name(&self) -> &str {
153 match self.capture_type {
154 CaptureType::PipeWire => "PipeWire",
155 CaptureType::XComposite => "XComposite",
156 }
157 }
158}
159
160impl AsRef<SourceInfo> for LinuxGeneralWindowCapture {
161 fn as_ref(&self) -> &SourceInfo {
162 &self.info
163 }
164}